home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 22
/
Cream of the Crop 22.iso
/
program
/
cgazv4n2.zip
/
EXPANDWI.C
< prev
next >
Wrap
Text File
|
1989-11-03
|
25KB
|
567 lines
/********************************** EXPANDWI.C ********************************
* Expand wildcard file names in a "ragged" string array into a new array.
*
* SYNOPSIS: int expandwild(pnOut, pvOut, nIn, vIn, attrib)
*
* nIn: no. of strings in vIn; if < 0, stop when vIn[i] == NULL.
*
* vIn: array of pointers to strings, e.g., argv. Wildcard strings
* expanded to full file name and path in *pvOut, all strings which
* don't expand are copied unchanged.
*
* attrib: attribute byte of files to be returned, see FA_xxxxxx constants.
*
* pnOut: points to int to return no. of strings in pvOut.
*
* pvOut: points to pointer to output array, *pvOut[*pnOut] == NULL.
*
* Return value: 0 if successful, non-0 (see codes below) if errors.
*
* AUTHOR: Bill Lee, 5132 106A St., Edmonton, Alberta, Canada T6H 2W7
* CompuServe 70441,2372.
*
* Copyright (C) by Bill Lee, 1989. All rights reserved.
* Use freely for any non-military purpose but retain copyright notice.
*
* COMPILATION OPTIONS: All memory models of two compilers are supported;
* for your compiler, define the appropriate constant if needed:
* + (none, the assumed default): Microsoft C 5.1 for DOS only.
* + OS2: With Microsoft C 5.1, use OS/2 v1.1 family API (FAPI) for the
* find-file functions; can then be "bound" to run under DOS or under
* protected mode. Header files from Microsoft OS/2 Presentation Manager
* Toolkit v1.1 needed. expandwi.c is probably re-entrant for
* multithreading but not tested.
* + (none, detects __TURBOC__): Turbo C 2.0 under DOS.
* + Define MAIN to generate a main() test driver.
*
* VERSION: 1.1, 89aug26.
***************************************************************************/
#if defined(__TURBOC__)
#include <string.h>
#include <stdio.h>
#include <dos.h>
#include <alloc.h>
/* Normally, dir.h is included for the prototypes of findfirst()
and findnext(), but it was necessary to code them below with a
modification for the MSC structure name which is used here. */
int _Cdecl findfirst(char *, struct find_t *, int);
int _Cdecl findnext(struct find_t *);
/* MSC 5.1 and TurboC 2.0 provide the directory find-first and find-next
file functions in slightly different flavours; the following macros
adapt the MSC DOS functions calls for use with the TurboC RTL. */
#define _dos_findfirst(path, attr, buff) findfirst((path), (buff), (attr))
#define _dos_findnext(buff) findnext((buff))
/* Structure used by _dos_findfirst() and _dos_findnext(), a-la MSC */
struct find_t {
char reserved[21]; /* workarea */
char attrib; /* file attribute bit flags */
unsigned int wr_time; /* packed time of last file write */
unsigned int wr_date; /* packed date of last file write */
long size; /* file size */
char name[13]; /* file name.ext, no blanks */
};
#else /* MSC */
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <malloc.h>
#if !defined(OS2)
#include <dos.h> /* _dos_findfirst/_dos_findnext */
#else
#define INCL_DOSFILEMGR /* OS/2 file mgmt */
#define INCL_DOSMISC /* for DosGetMachineMode() */
#define INCL_NOPM /* save compile time, exclude PM */
#include <os2.h> /* MS Pres Mgr Toolkit */
#endif /* #if !defined(OS2) */
/* MSC lacks Turbo C's stpcpy(); this macro is an adequate
replacement for non-overlapping src and dest. */
#define stpcpy(dest, src) (strcpy((dest), (src)) + strlen(src))
#endif /* #if defined(__TURBOC__) */
/* Maximum lengths of fnsplitE() input and output strings including \0. */
#define MAXPATH 80 /* input maximum */
#define MAXDRIVE 3
#define MAXDIR 66
#define MAXFILE 9
#define MAXEXT 5
/* Bit masks for fnsplitE() return value */
#define WILDCARDS 0x01 /* '*' and/or '?' in filename or ext */
#define EXTENSION 0x02 /* extension present */
#define FILENAME 0x04 /* filename present */
#define DIRECTORY 0x08 /* directory present */
#define DRIVE 0x10 /* drive: present */
/* Bit masks for returning truncation errors in fnsplitE() */
#define PATHt 0x0100 /* input path truncated */
#define EXTENSIONt (EXTENSION << 8) /* extension truncated */
#define FILENAMEt (FILENAME << 8) /* filename truncated */
#define DIRECTORYt (DIRECTORY << 8) /* directory truncated */
#define DRIVEt (DRIVE << 8) /* drive truncated (can't happen) */
#define FNSPLITEt (PATHt | EXTENSIONt | FILENAMEt | DIRECTORYt | DRIVEt)
/* all of the above */
/* Function prototypes */
int expandwild (int *, char **[], int, char *[], int);
int fnsplitE (char *, char *, char *, char *, char *);
int fnsplitEa (char *, char *, char *, unsigned int, int);
/* Codes returned for errors detected by expandwild().
(Remember to update rmsg[] in main() if these codes are changed.) */
#define EXWINOMEM 1 /* not enough memory */
#define EXWICNTCHG 2 /* count changed between passes */
#define EXWISIZCHG 3 /* array size changed between passes */
/*---------------------------------------------------------------------------*/
int expandwild(int *pnOut, /* ptr to no. of strings in output array */
char **pvOut[], /* ptr to output array */
int nIn, /* no. of strings in vIn */
char *vIn[], /* input string array */
int attrib) /* file attribute byte */
{
char drive[MAXDRIVE]; /* fnsplitE() workarea */
char dir[MAXDIR]; /* fnsplitE() workarea */
#if !defined(OS2) /* use DOS directory search */
struct find_t fblk; /* for _dos_findfirst()/_dos_findnext() */
#else /* use OS/2 directory search */
BYTE bMachineMode; /* for DosGetMachineMode() */
FILEFINDBUF fblk; /* for DosFindFirst()/DosFindNext() */
HDIR hDir; /* handle for directory search */
USHORT usSrchCnt; /* count: elements in fblk */
#endif
char *pvIn; /* work ptr into vIn[] */
char **vOut = NULL; /* anchor for new output array */
char *vOutEnd; /* last byte of new array */
char *pvTmp; /* temp ptr to vOut[] */
unsigned int iIn; /* index into vIn[] */
unsigned int vOutSize; /* accumulate memory size of new array */
unsigned int nOut; /* count strings in new array */
unsigned int iOut; /* string index in new array */
unsigned int fixSize; /* size of fixed part of a new string */
int fnsFlg; /* flags returned by fnsplitE() */
int err = 0; /* error code returned by expandwild() */
vOutSize = sizeof(pvOut[0]); /* space for NULL ptr in new array */
#if defined(OS2)
DosGetMachineMode(&bMachineMode); /* returns MODE_PROTECTED or MODE_REAL */
#endif /* #if defined(OS2) */
/* Determine no. and total size of strings required for new array. */
for (nOut = 0, iIn = 0;
(pvIn = vIn[iIn]) != NULL && (nIn < 0 || (nIn >= 0 && iIn < nIn));
iIn++) {
/* If no truncation and if a wildcard and if a file name is found,
save space for expanded filename string. */
#if !defined(OS2) /* DOS-only functions for finding files */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS && !_dos_findfirst(pvIn, attrib, &fblk)) {
fixSize = sizeof(pvOut[0]) + strlen(drive) + strlen(dir) + 1;
do {
nOut++;
vOutSize += fixSize + strlen(fblk.name);
} while (!_dos_findnext(&fblk));
}
#else /* OS/2 functions for finding files */
/* Set hDir based on mode, so protected mode can multithread. */
if (bMachineMode == MODE_PROTECTED)
hDir = HDIR_CREATE; /* so protected mode can multithread */
else /* bMachineMode == MODE_REAL */
hDir = HDIR_SYSTEM; /* required for DOS directory search */
usSrchCnt = 1; /* initialize count */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS
&& !DosFindFirst(pvIn, &hDir, attrib, &fblk, sizeof(fblk),
&usSrchCnt, 0L)) /* open handle, find 1st file */ {
fixSize = sizeof(pvOut[0]) + strlen(drive) + strlen(dir) + 1;
do {
nOut++;
vOutSize += fixSize + strlen(fblk.achName);
} while (!DosFindNext(hDir, &fblk, sizeof(fblk), &usSrchCnt));
DosFindClose(hDir); /* close directory search handle */
}
#endif /* #if !defined(OS2) */
else { /* save space for non-expanded string */
nOut++;
vOutSize += sizeof(pvOut[0]) + strlen(pvIn) + 1;
}
} /* for (iIn = 0; nOut = 0; ... ) */
/* Note that the logic above and below does not call DosFindClose() when
DosFindFirst() returns an error code. This appears to be ok in v1.1
of OS/2 as DosFindFirst() returns an invalid handle which cannot be
closed anyway. But you might watch it in future releases. */
/* Allocate memory for new array. */
if ((vOut = (char **) malloc(vOutSize)) == NULL)
err = EXWINOMEM; /* not enough memory */
else {
vOut[0] = (char *) (vOut + nOut + 1); /* ptr to 1st string */
vOutEnd = (char *) vOut + vOutSize; /* 1st byte after last */
}
/* Go through again and copy strings to new array. */
for (iOut = 0, iIn = 0; err == 0
&& (pvIn = vIn[iIn]) != NULL && (nIn < 0 || (nIn >= 0 && iIn < nIn));
iIn++) {
/* If no truncation and if wildcard and if file name is found,
save expanded string. */
#if !defined(OS2) /* DOS-only functions for finding files */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS && !_dos_findfirst(pvIn, attrib, &fblk)) {
fixSize = strlen(drive) + strlen(dir) + 1;
do {
if (iOut >= nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count too big */
else {
pvTmp = vOut[iOut];
if (pvTmp + fixSize + strlen(fblk.name) > vOutEnd)
err = EXWISIZCHG; /* overflow */
else /* copy full name, save ptr to next new string */
vOut[++iOut] = 1 + /* whew! */
stpcpy(stpcpy(stpcpy(pvTmp,drive),dir),fblk.name);
}
} while (err == 0 && !_dos_findnext(&fblk));
}
#else /* OS/2 functions for finding files */
/* Set hDir based on mode, so protected mode can multithread. */
if (bMachineMode == MODE_PROTECTED)
hDir = HDIR_CREATE; /* so protected mode can multithread */
else /* bMachineMode == MODE_REAL */
hDir = HDIR_SYSTEM; /* required for DOS directory search */
usSrchCnt = 1; /* initialize count */
if (!((fnsFlg = fnsplitE(pvIn, drive, dir, NULL, NULL)) & FNSPLITEt)
&& fnsFlg & WILDCARDS
&& !DosFindFirst(pvIn, &hDir, attrib, &fblk, sizeof(fblk),
&usSrchCnt, 0L)) /* re-open handle, find 1st file */ {
fixSize = strlen(drive) + strlen(dir) + 1;
do {
if (iOut >= nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count too big */
else {
pvTmp = vOut[iOut];
if (pvTmp + fixSize + strlen(fblk.achName) > vOutEnd)
err = EXWISIZCHG; /* overflow */
else /* copy full name, save ptr to next new string */
vOut[++iOut] = 1 + /* whew! */
stpcpy(stpcpy(stpcpy(pvTmp,drive),dir),fblk.achName);
}
} while (err == 0
&& !DosFindNext(hDir, &fblk, sizeof(fblk), &usSrchCnt));
DosFindClose(hDir); /* close directory search handle */
}
#endif /* #if !defined(OS2) */
else { /* copy non-wildcard to new array */
if (iOut >= nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count too big */
else {
pvTmp = vOut[iOut];
if (pvTmp + strlen(pvIn) + 1 > vOutEnd)
err = EXWISIZCHG; /* overflow */
else /* copy non-wildcard, save ptr to next new string*/
vOut[++iOut] = 1 + stpcpy(pvTmp, pvIn);
}
}
} /* for (iIn = 0; iOut = 0; ... ) */
if (err == 0)
/* Check for changes in count or size between 2 passes thru names. */
if (iOut != nOut) /* count changed? */
err = EXWICNTCHG; /* 2nd pass count smaller */
else if (vOut[nOut] != vOutEnd) /* size changed? */
err = EXWISIZCHG; /* 2nd pass size smaller */
if (err == 0) { /* if no error, return results to caller */
vOut[nOut] = NULL; /* terminate array as per ANSI standard */
*pnOut = nOut;
*pvOut = vOut;
}
else if (vOut != NULL)
free((char *) vOut);
return (err);
} /* expandwild() */
/*---------------------------------------------------------------------------*/
/* fnsplitE():
Split full file name path string into component parts--drive, directory,
file name, and file name extension. Returns bit flags indicating which
parts are in the input and whether any input was truncated because it is
too long. This is a work-alike function of fnsplit() in Turbo C 1.0 except
the latter does not indicate truncation errors. */
int fnsplitE(char *path, /* input path */
char *drive, /* output drive */
char *dir, /* output directory */
char *name, /* output file name */
char *ext) /* output file name extension */
{
int flags = 0; /* bit flags return value */
char *pL, *pR; /* Left & Right-hand ptrs into path */
unsigned int nchar; /* no. of characters */
/* Initialize output to null strings where present in case component
absent in input. */
if (drive != NULL) *drive = '\0';
if (dir != NULL) *dir = '\0';
if (name != NULL) *name = '\0';
if (ext != NULL) *ext = '\0';
while (*path == ' ') /* skip any leading spaces in input */
++path;
/* Truncate input if too long.
(Turbo C fnsplit() uses MAXPATH, not MAXPATH - 1.) */
if ((nchar = strlen(path)) > MAXPATH - 1) {
nchar = MAXPATH - 1;
flags |= PATHt; /* indicate truncation error */
}
/* Return if path null; only needed if path at segment offset 0. */
if (nchar == 0)
return(flags);
/* Initialize current Left and Right ends to the terminal \0. */
pL = pR = path + nchar;
/* Reverse scan thru input path string. */
while (--pL >= path) {
/* Check for special character in input path string. */
switch (*pL) {
case '.': /* possible left-most char of extension */
/* Extension, only if we're not already past it. */
if (!(flags & (DRIVE | DIRECTORY | FILENAME | EXTENSION))) {
flags |= fnsplitEa(pL, pR, ext, MAXEXT - 1, EXTENSION);
pR = pL;
}
break;
case '*': /* wildcard if in filename or extension */
case '?':
if (!(flags & (DRIVE | DIRECTORY)))
flags |= WILDCARDS;
break;
case '\\': /* possible directory indication */
case '/':
if (!(flags & (DRIVE | DIRECTORY | FILENAME))) {
flags |= fnsplitEa(++pL, pR, name, MAXFILE - 1, FILENAME);
pR = pL--;
}
if (!(flags & (DRIVE | DIRECTORY)))
flags |= DIRECTORY;
break;
case ':': /* right-most char of 2-char drive? */
if (pL == path + 1) { /* colon must be here to be a drive */
if (!(flags & (DRIVE | DIRECTORY | FILENAME))) {
flags |= fnsplitEa(++pL, pR, name, MAXFILE - 1,
FILENAME);
pR = pL--;
}
else if (flags & DIRECTORY) {
flags |= fnsplitEa(++pL, pR, dir, MAXDIR - 1,
DIRECTORY);
pR = pL--;
}
flags |= DRIVE;
}
} /* switch (*pL) */
/* If back to start of input string, deal with input that remains. */
if (pL == path) {
if (!(flags & (DRIVE | DIRECTORY | FILENAME)))
flags |= fnsplitEa(pL, pR, name, MAXFILE - 1, FILENAME);
else if (flags & DRIVE)
flags |= fnsplitEa(pL, pR, drive, MAXDRIVE - 1, DRIVE);
else /* only dir remains */
flags |= fnsplitEa(pL, pR, dir, MAXDIR - 1, DIRECTORY);
break; /* stop loop if path segment offset == 0 */
}
} /* while (--pL >= path) */
return (flags);
} /* fnsplitE() */
/* fnsplitEa() copies string to output using left/right ptrs to input. */
int fnsplitEa(char *pL, /* Left-hand ptr to input */
char *pR, /* Right-hand ptr to input */
char *pOut, /* ptr to output, can be NULL */
unsigned int maxOut, /* max non-null char to *pOut excluding \0 */
int flag) /* fnsplitE() flag bit */
{
unsigned int nchar; /* no. of characters */
if ((nchar = pR - pL) > maxOut) { /* too long for output? */
nchar = maxOut; /* yes, truncate */
flag |= flag << 8; /* indicate truncation to caller */
}
if (pOut != NULL) { /* any output area? */
strncpy(pOut, pL, nchar); /* copy to output */
*(pOut + nchar) = '\0'; /* terminal \0 */
}
return (nchar > 0 ? flag : 0); /* return flag if input not empty */
} /* fnsplitEa() */
/*---------------------------------------------------------------------------*/
#if defined(MAIN)
/* main() test driver for expandwild(). */
/* File attributes a-la MSC for TC or for use with OS/2 */
#if defined(__TURBOC__) || defined(OS2)
#define _A_NORMAL 0x00 /* Normal file--no attribute bits */
#define _A_RDONLY 0x01 /* Read only attribute */
#define _A_HIDDEN 0x02 /* Hidden file */
#define _A_SYSTEM 0x04 /* System file */
#define _A_VOLID 0x08 /* Volume label--ignored for OS/2 */
#define _A_SUBDIR 0x10 /* Subdirectory */
#define _A_ARCH 0x20 /* Archive */
#endif /* #if defined(__TURBOC__) */
#if defined(OS2)
#define _A_ALLBITS (_A_RDONLY|_A_HIDDEN|_A_SYSTEM| _A_SUBDIR|_A_ARCH)
#else
#define _A_ALLBITS (_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_VOLID|_A_SUBDIR|_A_ARCH)
#endif
unsigned int xtoi(char []);
int main(int argc,
char *argv[]) /* argv[1] is attribute byte in ASCII hex */
{
unsigned int attrib; /* attribute byte from argv[1] via xtoi() */
int i; /* argv/newargv index */
int rc; /* return code from expandwild() */
int newargc; /* count of expanded arg strings */
char **newargv; /* ptr to new array of expanded arg strings */
/* Msgs for errors detected by expandwild(). */
static char *rmsg[] = {
"successful", /* 0 */
"not enough memory for output", /* 1 EXWINOMEM */
"output count change between passes", /* 2 EXWICNTCHG */
"output size change between passes" /* 3 EXWISIZCHG */
};
#if !defined(__TURBOC__)
static char compiler[] = "Microsoft C";
#if defined(M_I86SM)
static char model[] = "small model";
#elif defined(M_I86MM)
static char model[] = "medium model";
#elif defined(M_I86CM)
static char model[] = "compact model";
/* Test M_I86HM before M_I86LM because both defined in huge model. */
#elif defined(M_I86HM)
static char model[] = "huge model";
#elif defined(M_I86LM)
static char model[] = "large model";
#endif
#else
static char compiler[] = "Turbo C";
#if defined(__TINY__)
static char model[] = "tiny model";
#elif defined(__SMALL__)
static char model[] = "small model";
#elif defined(__MEDIUM__)
static char model[] = "medium model";
#elif defined(__COMPACT__)
static char model[] = "compact model";
#elif defined(__LARGE__)
static char model[] = "large model";
#elif defined(__HUGE__)
static char model[] = "huge model";
#endif
#endif
fprintf(stderr, "Test expandwild(), %s %s"
#if defined(OS2)
" (OS/2)"
#endif
"\n", compiler, model);
/* Check range of no. of args and value of attribute byte. */
if (argc < 2 || (attrib = xtoi(argv[1])) == 0xffff
|| attrib & (_A_ALLBITS ^ 0xffff)) {
printf("\nUsage: expandwi attrib [filespec [filespec] ...]\n");
printf(" where attrib is file attribute byte in hex.\n");
return (255);
}
printf("\nFile names requested with attribute byte of 0x%X\n", attrib);
printf("\nexpandwild() input strings (count = %d)\n", argc - 2);
for (i = 2; i < argc; i++)
printf(" %s\n", argv[i]);
rc = expandwild(&newargc, &newargv, -1, &argv[2], attrib);
printf("\nexpandwild() return code = %d (%s)\n", rc, rmsg[rc]);
if (rc == 0) {
printf("\nexpandwild() expanded output strings (count = %d)\n",
newargc);
for (i = 0; i < newargc; i++)
printf(" %s\n", newargv[i]);
}
return (rc);
} /* main() */
/* Convert ASCII string of hex digits to int. Return 0xffff if invalid digits
or overflow detected; warning: also returned for ASCII string "FFFF". */
#include <ctype.h>
unsigned int xtoi(char x[]) /* x is input ASCII hex string, can be null */
{
unsigned int xw; /* work area for current hex digit */
unsigned int ix = 0; /* return value accumulator */
for (; *x != '\0'; ++x) {
if (!isascii(xw = *x))
return (0xffff); /* invalid digit */
xw = toupper(xw);
if (xw >= '0' && xw <= '9')
xw -= '0';
else if (xw >= 'A' && xw <= 'F')
xw -= 'A' - 0xA;
else
return (0xffff); /* invalid digit */
if (ix < 0x1000) { /* overflow? */
ix <<= 4; /* make room for current digit */
ix |= xw; /* add current digit */
}
else
return (0xffff); /* overflow */
}
return (ix);
} /* xtoi() */
#endif /* #if defined(MAIN) */
/*---------------------------------------------------------------------------*/